﻿using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using XUCore.Helpers;

namespace XUCore.Serializer
{
    /// <summary>
    /// json文档
    /// </summary>
    [Serializable]
    public class Bson : IDictionary<string, object>, IDictionary, IXmlSerializable
    {
        private readonly List<string> _orderedKeys;
        private readonly Dictionary<string, object> _dictionary;
        private readonly IComparer<string> _keyComparer;
        private readonly IEqualityComparer<string> _equalityComparer;

        public Bson()
        {
            _dictionary = new Dictionary<string, object>();
            _orderedKeys = new List<String>();
        }

        public Bson(IEqualityComparer<string> equalityComparer)
        {
            _dictionary = new Dictionary<string, object>(equalityComparer);
            _orderedKeys = new List<String>();
        }
        /// <summary>
        /// 指定排序规则和检索规则
        /// </summary>
        /// <param name="keyComparer">排序规则</param>
        /// <param name="equalityComparer">检索规则</param>
        /// <exception cref="ArgumentNullException"></exception>
        public Bson(IComparer<string> keyComparer, IEqualityComparer<string> equalityComparer)
            : this(equalityComparer)
        {
            if (keyComparer == null)
                throw new ArgumentNullException("keyComparer");

            _equalityComparer = equalityComparer;
            _keyComparer = keyComparer;
        }

        public Bson(string key, object value)
            : this()
        {
            Add(key, value);
        }

        public Bson(IEnumerable<KeyValuePair<string, object>> dictionary)
            : this()
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");

            foreach (var entry in dictionary)
                Add(entry.Key, entry.Value);
        }
        /// <summary>
        /// 创建新的节点（区分大小写）
        /// </summary>
        public static Bson Node => Create(false);
        /// <summary>
        /// 创建新的节点（不区分大小写）
        /// </summary>
        public static Bson CNode => Create(true);
        /// <summary>
        /// 创新新的节点
        /// </summary>
        /// <param name="ignoreCase">忽略大小写</param>
        /// <returns></returns>
        public static Bson Create(bool ignoreCase = false) => ignoreCase ? new Bson(StringComparer.OrdinalIgnoreCase, StringComparer.OrdinalIgnoreCase) : new();
        /// <summary>
        /// 创建节点集合
        /// </summary>
        public static List<Bson> Nodes => new();

        public object this[string key]
        {
            get { return Get(key); }
            set { Set(key, value); }
        }

        ICollection IDictionary.Keys
        {
            get { return _dictionary.Keys; }
        }

        ICollection IDictionary.Values
        {
            get { return _dictionary.Values; }
        }

        public ICollection<object> Values
        {
            get { return _dictionary.Values; }
        }

        public ICollection<string> Keys
        {
            get { return _orderedKeys.AsReadOnly(); }
        }

        public object Get(string key)
        {
            object item;
            return _dictionary.TryGetValue(key, out item) ? item : null;
        }

        public T Get<T>(string key)
        {
            var value = Get(key);
            if (value == null)
                return default(T);
            return Conv.To<T>(value);
        }

        public bool TryGetValue(string key, out object value)
        {
            return _dictionary.TryGetValue(key, out value);
        }

        public bool TryGetValue<T>(string key, out T value)
        {
            var res = _dictionary.TryGetValue(key, out object obj);

            value = Conv.To<T>(obj);

            return res;
        }
        /// <summary>
        /// 是否存在Key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool ContainsKey(string key)
        {
            return _dictionary.ContainsKey(key);
        }
        /// <summary>
        /// 添加键值对
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Bson Add(string key, object value)
        {
            _dictionary.Add(key, value);
            _orderedKeys.Add(key);//Relies on ArgumentException from above if key already exists.
            EnsureKeyOrdering();
            return this;
        }
        /// <summary>
        /// 添加键值对
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Bson Add(bool condition, string key, object value)
        {
            if (!condition)
                return this;

            return Add(key, value);
        }
        /// <summary>
        /// 写入集合
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Bson Batch(string key, params Bson[] value)
        {
            return Add(key, value);
        }
        /// <summary>
        /// 添加键值对
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        void IDictionary<string, object>.Add(string key, object value)
        {
            Add(key, value);
        }
        /// <summary>
        /// 写入值，不存在则添加，存在则覆盖
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Bson Set(string key, object value)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            if (_equalityComparer == null)
            {
                if (!_orderedKeys.Contains(key))
                    _orderedKeys.Add(key);
            }
            else
            {
                if (!_orderedKeys.Contains(key, _equalityComparer))
                    _orderedKeys.Add(key);
            }

            _dictionary[key] = value;

            EnsureKeyOrdering();

            return this;
        }
        /// <summary>
        /// 写入值，不存在则添加，存在则覆盖
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Bson Set(bool condition, string key, object value)
        {
            if (!condition)
                return this;

            return Set(key, value);
        }
        /// <summary>
        /// 写入键值对
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="position"></param>
        public Bson Insert(string key, object value, int position)
        {
            _dictionary.Add(key, value);//Relies on ArgumentException from above if key already exists.
            _orderedKeys.Insert(position, key);
            EnsureKeyOrdering();

            return this;
        }
        /// <summary>
        /// 写入键值对
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="position"></param>
        public Bson Insert(bool condition, string key, object value, int position)
        {
            if (!condition)
                return this;

            _dictionary.Add(key, value);//Relies on ArgumentException from above if key already exists.
            _orderedKeys.Insert(position, key);
            EnsureKeyOrdering();

            return this;
        }
        /// <summary>
        /// 往前插入键值对
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Bson Prepend(string key, object value)
        {
            return Insert(key, value, 0);
        }
        /// <summary>
        /// 往前插入键值对
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Bson Prepend(bool condition, string key, object value)
        {
            if (!condition)
                return this;

            return Insert(key, value, 0);
        }
        /// <summary>
        /// 合并Bson
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public Bson Merge(Bson source)
        {
            if (source == null)
                return this;

            Update(this, source);

            return this;
        }
        /// <summary>
        /// 合并Bson
        /// </summary>
        /// <param name="source"></param>
        /// <param name="dest"></param>
        /// <returns></returns>
        private void Update(Bson source, Bson dest)
        {
            if (dest == null) return;

            foreach (var key in dest.Keys)
            {
                if (dest[key] is Bson _dest && source[key] is Bson _source)
                {
                    Update(_source, _dest);
                }
                else
                {
                    source[key] = dest[key];
                }
            }
        }
        /// <summary>
        /// 删除键值对
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Remove(string key)
        {
            _orderedKeys.Remove(key);
            return _dictionary.Remove(key);
        }

        void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
        {
            Add(item.Key, item.Value);
        }

        bool IDictionary.Contains(object key)
        {
            if (_equalityComparer == null)
                return _orderedKeys.Contains(Convert.ToString(key));
            return _orderedKeys.Contains(Convert.ToString(key), _equalityComparer);
        }

        void IDictionary.Add(object key, object value)
        {
            Add(Convert.ToString(key), value);
        }

        public void Clear()
        {
            _dictionary.Clear();
            _orderedKeys.Clear();
        }

        IDictionaryEnumerator IDictionary.GetEnumerator()
        {
            return ((IDictionary)_dictionary).GetEnumerator();
        }

        void IDictionary.Remove(object key)
        {
            Remove(Convert.ToString(key));
        }

        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="key">1级Key</param>
        /// <returns></returns> 
        object IDictionary.this[object key]
        {
            get { return Get(Convert.ToString(key)); }
            set { Set(Convert.ToString(key), value); }
        }

        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="k1">1级Key</param>
        /// <param name="k2">2级Key</param>
        /// <returns></returns> 
        public object this[string k1, string k2]
        {
            get
            {
                return Get<Bson>(k1)?.Get(k2);
            }
            set
            {
                Get<Bson>(k1)?.Set(k2, value);
            }
        }

        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="k1">1级Key</param>
        /// <param name="k2">2级Key</param>
        /// <param name="k3">3级Key</param>
        /// <returns></returns> 
        public object this[string k1, string k2, string k3]
        {
            get
            {
                return Get<Bson>(k1)?.Get<Bson>(k2)?.Get(k3);
            }
            set
            {
                Get<Bson>(k1)?.Get<Bson>(k2)?.Set(k3, value);
            }
        }

        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="k1">1级Key</param>
        /// <param name="k2">2级Key</param>
        /// <param name="k3">3级Key</param>
        /// <param name="k4">4级Key</param>
        /// <returns></returns> 
        public object this[string k1, string k2, string k3, string k4]
        {
            get
            {
                return Get<Bson>(k1)?.Get<Bson>(k2)?.Get<Bson>(k3)?.Get(k4);
            }
            set
            {
                Get<Bson>(k1)?.Get<Bson>(k2)?.Get<Bson>(k3)?.Set(k4, value);
            }
        }
        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="k1">1级Key</param>
        /// <param name="k2">2级Key</param>
        /// <param name="k3">3级Key</param>
        /// <param name="k4">4级Key</param>
        /// <param name="k5">5级Key</param>
        /// <returns></returns> 
        public object this[string k1, string k2, string k3, string k4, string k5]
        {
            get
            {
                return Get<Bson>(k1)?.Get<Bson>(k2)?.Get<Bson>(k3)?.Get<Bson>(k4)?.Get(k5);
            }
            set
            {
                Get<Bson>(k1)?.Get<Bson>(k2)?.Get<Bson>(k3)?.Get<Bson>(k4)?.Set(k5, value);
            }
        }

        bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
        {
            return ((IDictionary<string, object>)_dictionary).Contains(item);
        }

        void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
        {
            ((ICollection<KeyValuePair<string, object>>)_dictionary).CopyTo(array, arrayIndex);
        }

        public void CopyTo(Bson destinationDocument)
        {
            if (destinationDocument == null)
                throw new ArgumentNullException("destinationDocument");

            //Todo: Fix any accidental reordering issues.

            foreach (var key in _orderedKeys)
            {
                if (destinationDocument.ContainsKey(key))
                    destinationDocument.Remove(key);
                destinationDocument[key] = this[key];
            }
        }

        public bool Remove(KeyValuePair<string, object> item)
        {
            var removed = ((ICollection<KeyValuePair<string, object>>)_dictionary).Remove(item);
            if (removed)
                _orderedKeys.Remove(item.Key);
            return removed;
        }

        void ICollection.CopyTo(Array array, int index)
        {
            ((ICollection)_dictionary).CopyTo(array, index);
        }

        public int Count
        {
            get { return _dictionary.Count; }
        }

        object ICollection.SyncRoot
        {
            get { return _orderedKeys; /* no special object is need since _orderedKeys is internal.*/ }
        }

        bool ICollection.IsSynchronized
        {
            get { return false; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        bool IDictionary.IsFixedSize
        {
            get { return false; }
        }

        public override bool Equals(object obj)
        {
            if (obj is Bson)
                return Equals(obj as Bson);
            return base.Equals(obj);
        }

        public bool Equals(Bson document)
        {
            if (document == null)
                return false;
            if (_orderedKeys.Count != document._orderedKeys.Count)
                return false;
            return GetHashCode() == document.GetHashCode();
        }

        public override int GetHashCode()
        {
            var hash = 27;
            foreach (var key in _orderedKeys)
            {
                var valueHashCode = GetValueHashCode(this[key]);
                unchecked
                {
                    hash = (13 * hash) + key.GetHashCode();
                    hash = (13 * hash) + valueHashCode;
                }
            }
            return hash;
        }

        private int GetValueHashCode(object value)
        {
            if (value == null)
                return 0;
            return (value is Array) ? GetArrayHashcode((Array)value) : value.GetHashCode();
        }

        private int GetArrayHashcode(Array array)
        {
            var hash = 0;
            foreach (var value in array)
            {
                var valueHashCode = GetValueHashCode(value);
                unchecked
                {
                    hash = (13 * hash) + valueHashCode;
                }
            }
            return hash;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
        {
            return _orderedKeys.Select(orderedKey => new KeyValuePair<string, object>(orderedKey, _dictionary[orderedKey])).GetEnumerator();
        }

        public Dictionary<string, object> ToDictionary()
        {
            return new Dictionary<string, object>(this);
        }

        public override string ToString()
        {
            return this.ToJson();
        }
        /// <summary>
        /// 将对象转换为Json字符串
        /// </summary>
        /// <param name="isConvertToSingleQuotes">是否将双引号转换成单引号</param>
        /// <param name="camelCase">是否驼峰式命名</param>
        /// <param name="indented">是否缩进</param>
        public string ToString(bool isConvertToSingleQuotes = false, bool camelCase = false,
            bool indented = false)
        {
            return this.ToJson(isConvertToSingleQuotes, camelCase, indented);
        }
        /// <summary>
        /// 将对象转换为Json字符串
        /// </summary>
        /// <param name="settings">json配置</param>
        public string ToString(JsonSerializerSettings settings)
        {
            return this.ToJson(settings);
        }

        private void EnsureKeyOrdering()
        {
            if (_keyComparer == null)
                return;

            _orderedKeys.Sort(_keyComparer);
        }


        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            reader.ReadStartElement();

            while (reader.IsStartElement())
            {
                var key = reader.Name;
                object value = null;

                if (reader.MoveToAttribute("type"))
                {
                    var type = Type.GetType(reader.Value);

                    reader.ReadStartElement();

                    var serializer = new XmlSerializer(type);
                    value = serializer.Deserialize(reader);
                }
                else
                    reader.Read();

                Add(key, value);
            }
        }

        public void WriteXml(XmlWriter writer)
        {
            WriteDoc(writer, this);
        }

        private void WriteDoc(XmlWriter writer, Bson doc)
        {
            foreach (string key in doc.Keys)
            {
                writer.WriteStartElement(key);
                if (doc[key] is Bson)
                {
                    Bson subDoc = (Bson)doc[key];
                    WriteDoc(writer, subDoc);
                }
                else if (doc[key] is List<Bson>)
                {
                    List<Bson> subDoc = (List<Bson>)doc[key];
                    for (int i = 0; i < subDoc.Count; i++)
                    {
                        writer.WriteStartElement("Item");
                        WriteDoc(writer, subDoc[i]);
                        writer.WriteEndElement();
                    }
                }
                else
                {
                    writer.WriteValue(doc[key] == null ? "" : doc[key]);
                }
                writer.WriteEndElement();
            }
        }

    }
}
